-- TalentM
-- Created by chengb Feb/10/2015
-- 天赋模块

require "game/logic/base/Talent"

module("TalentM", package.seeall);

-- 天赋图标配置信息
local talentPosList = {};

-- 内部函数声明
local whenGainPet;
local doActivateTalent;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    local talent_pos_CSV = get_talent_pos_CSV();
    for i = 1, #talent_pos_CSV do
        talentPosList[talent_pos_CSV[i].id] = parsePosition(talent_pos_CSV[i].position);
    end
end

-- 初始化
function init()
    loadCsv();

    -- 关注获得冈布奥的事件
    EventMgr.removeAll("TalentM");
    EventMgr.register("TalentM", event.GAIN_PET, function(classId)
        whenGainPet(classId);
    end);
end

-----------------------------------
-- 检索天赋配置
-- @param skillId 天赋技能id
-- @param path    字段
function queryTalent(skillId, path)
    return SkillM.query(skillId, path);
end

------------------------------------
-- 解析天赋图标位置信息
function parsePosition(info)
    local arr = string.explode(info, ",");
    return arr;
end

-----------------------------------
-- 获取天赋图标位置
-- @param index 第几个图标
function getPosition(index)
    return talentPosList[index];
end

------------------------------------
-- 获取所有的天赋配置
function getTalentList()
    local skillTable = SkillM.getSkillTable();
    local ret = {};

    local talents = ME.user:getTalents();

    for _, row in pairs(skillTable) do
        local skillId = row["skill_id"];
        if isTalent(skillId) and row.level == 1 then
            for i = 1, #talents do
                local actSkillId = talents[i];
                -- 某个技能已经升级为高级，此处需要替换下
                if SkillM.findBaseSkill(actSkillId) == skillId then
                    skillId = actSkillId;
                    break;
                end
            end

            table.insert(ret, skillId);
        end
    end

    return ret;
end

---------------------------------
-- 是否是天赋
-- @param skillId 天赋技能id
function isTalent(skillId)
    if isUserTalent(skillId) or
        isPetTalent(skillId) or
        isAirshipRings(skillId) then
        return true;
    end
    return false;
end

---------------------------------
-- 是否是玩家天赋技能
-- @param skillId 天赋技能id
function isUserTalent(skillId)
	return queryTalent(skillId, "type") == USER_TALENT;
end

---------------------------------
-- 是否是冈布奥天赋技能
-- @param skillId 天赋技能id
function isPetTalent(skillId)
    return queryTalent(skillId, "type") == PET_TALENT;
end

---------------------------------
-- 是否是飞艇光环技能
-- @param skillId 光环技能id
function isAirshipRings(skillId)
    return queryTalent(skillId, "type") == AIRSHIP_RINGS;
end

---------------------------------
-- 获取当前世界树孔数
function getWorldTreeHoleAmount()
    return ME.user.dbase:query("tree_hole", 0);
end

-----------------------------------
-- 计算购买世界树孔的消耗
function calcBuyWorldTreeHoleCost()
    local holeAmount = getWorldTreeHoleAmount();
    return FormulaM.invoke("CALC_BUY_WORLD_TREE_HOLE_COST", holeAmount);
end

-----------------------------------
-- 计算升级天赋的消耗
-- @param skillId 天赋技能id
function calcUpgradeTalentCost(skillId)
    local nextSkillId = queryTalent(skillId, "next");
    if nextSkillId == nil or nextSkillId == -1 then
        -- 该天赋不存在或者已经满级
        return {};
    end

    local nextLevel = queryTalent(nextSkillId, "level");
    if isUserTalent(skillId) then
        -- 玩家天赋
        return FormulaM.invoke("CALC_UPGRADE_USER_TALENT_COST", nextLevel);
    else
        return {};
    end
end

---------------------------------
-- 判断某个天赋是否已经激活
-- @param skillId  天赋技能id
function isAlreadyActive(skillId)
    local talents = ME.user:getTalents();
    if #talents == 0 then
        return false;
    end

    local baseSkill = SkillM.findBaseSkill(skillId);
    for _, id in pairs(talents) do
        if baseSkill == SkillM.findBaseSkill(id) then
            return true;
        end
    end

    return false;
end

---------------------------------
-- 判断能否激活天赋
-- @param skillId  天赋技能id
-- @return 如果判断通过，返回true，否则返回错误码
function canActivateTalent(skillId)
    local talentInfo = queryTalent(skillId);
    if nil == talentInfo then
        -- 该天赋不存在
        return false;
    end

    if not isTalent(skillId) then
        -- 不是天赋
        return false;
    end

    if isAlreadyActive(skillId) then
        -- 已经激活
        return false;
    end

    return true;
end

---------------------------------
-- 激活天赋
-- @param skillId   天赋技能id
-- @param isNotify  是否通知服务器激活
function activateTalent(skillId, isNotify)
    -- 经判断，不能激活
    if true ~= canActivateTalent(skillId) then
        return false;
    end

    if isNotify == true then
        -- 请求服务器激活
        Operation.cmd_activate_talent(skillId);
        return true;
    end

    -- 客户端自行激活
    doActivateTalent(skillId);

    if ME.isInGame then
        PropM.refreshEffect(ME.user, "skill");
    end
    return true;
end

---------------------------------
-- 执行激活天赋（这里不做能否激活的判断）
-- @param skillId  天赋技能id
function doActivateTalent(skillId)
    local talents = ME.user:getTalents();
    table.insert(talents, skillId);
    ME.user:setTalents(talents);

    -- 抛出天赋激活的事件
    EventMgr.fire(event.ACTIVATE_TALENT, {["skillId"] = skillId, })
end

---------------------------------
-- 判断能否升级天赋
-- @param skillId  天赋技能id
-- @return 如果判断通过，返回true，否则返回错误码
function canUpgradeTalent(skillId)
    local talentInfo = queryTalent(skillId);
    if nil == talentInfo then
        -- 该天赋不存在
        return false;
    end

    if not isTalent(skillId) then
        -- 不是天赋
        return false;
    end

    local talents = ME.user:getTalents();
    if table.indexOf(talents, skillId) == -1 then
        -- 该天赋尚未激活
        return false;
    end

    -- 获取下一级天赋技能id
    local nextSkillId = queryTalent(skillId, "next");
    if nextSkillId == -1 then
        -- 该天赋已达最高等级
    	return "level_limit";
    end

    -- 如果是冈布奥天赋，需要判断冈布奥阶位
    if isPetTalent(skillId) then
        local classId = PetM.getHeroByTalentId(skillId);
        local pet = PetM.getMyPet(classId);
        if pet ~= nil then
            if queryTalent(skillId, "level") >= pet:getRank() then
                return "pet_rank_limit";
            end
        end
    end

    -- 如果是飞艇光环，需要判断阶位
    if isAirshipRings(skillId) then
        local shipId = NewAirShipM.getAirshipByRingsId(skillId);
        if shipId ~= nil then
            local info = NewAirShipM.getUserAirShipInfoWithoutProp(shipId);
            local rank = info.rank or 0;
            if queryTalent(skillId, "level") >= rank then
                return "airship_rank_limit";
            end
        end
    end

    -- 计算升级天赋的消耗
    local cost = calcUpgradeTalentCost(skillId);
    if #cost == 0 then
        return true;
    end

    local ok = true;
    for _, arr in pairs(cost) do
        if arr[1] == 1 then
            -- 物品消耗
            ok = ItemM.getAmount(ME.user, arr[2]) >= arr[3];
            if not ok then
                -- 物品不足
                return "item_lack";
            end
        elseif arr[1] == 2 then
            -- 属性消耗
            ok = ME.user:queryAttrib(arr[2]) >= arr[3];
            if not ok then
                -- 属性不足
                return arr[2] .. "_lack";
            end
        end

        -- 有某一项要求没达到
        if not ok then
            return false;
        end
    end

    return true;
end

------------------------------
-- 升级天赋
-- @param skillId 天赋技能id
function upgradeTalent(skillId)
    -- 经判断，不能升级
    if true ~= canUpgradeTalent(skillId) then
        return false;
    end

    -- 请求服务器升级天赋
    Operation.cmd_upgrade_talent(skillId);

    return true;
end

-------------------------------
-- 判断能否选择天赋
-- @param skillId 天赋技能id
-- @return 如果判断通过，返回true，否则返回错误码
function canSelectTalent(skillId)
    local talentInfo = queryTalent(skillId);
    if nil == talentInfo then
        -- 该天赋不存在
        return false;
    end

    if not isTalent(skillId) then
        -- 不是天赋
        return false;
    end

    local talents = ME.user:getTalents();
    if table.indexOf(talents, skillId) == -1 then
        -- 该天赋尚未激活
        return false;
    end

    local talentsOption = ME.user:getTalentsOption();
    local holeAmount = getWorldTreeHoleAmount();
    if #talentsOption >= holeAmount then
        -- 世界树孔数不足
        return "hole_lack";
    end

    if table.indexOf(talentsOption, skillId) ~= -1 then
        -- 该天赋已经在天赋树上了
        return false;
    end

    return true;
end

----------------------------
-- 选择天赋
-- @param skillId  天赋技能id
function selectTalent(skillId)
    -- 经判断，不能选择该天赋
    if true ~= canSelectTalent(skillId) then
        return false;
    end

    -- 请求服务器选择天赋
    Operation.cmd_select_talent(skillId);

    return true;
end

---------------------------
-- 判断能否替换天赋
-- @param index    被替换的天赋位置
-- @param skillId  新的天赋技能id
function canReplaceTalent(index, skillId)
    -- 判断替换的位置是否合法
    local talentsOption = ME.user:getTalentsOption();
    if index < 1 or index > #talentsOption then
        return false;
    end

    local talents = ME.user:getTalents();
    if table.indexOf(talents, skillId) == -1 then
        -- 新的天赋尚未激活，不能替换
        return false;
    end

    if table.indexOf(talentsOption, skillId) ~= -1 then
        -- 新的天赋已经在天赋树上了，无法替换其他位置的天赋
        return false;
    end

    return true;
end

-----------------------------
-- 替换天赋
-- @param index    被替换的天赋位置
-- @param skillId  新的天赋技能id
function replaceTalent(index, skillId)
    -- 经判断，不能替换
    if true ~= canReplaceTalent(index, skillId) then
        return false;
    end

    -- 请求服务器替换
    Operation.cmd_replace_talent(index, skillId);

    return true;
end

----------------------------
-- 判断能否购买世界树孔
-- @return 如果判断通过，返回true，否则返回错误码
function canBuyWorldTreeHole()
    local holeAmount = getWorldTreeHoleAmount();
    if holeAmount >= FormulaM.invoke("USER_MAX_WORLD_TREE_HOLE", ME.user) then
        -- 世界树孔数已达上限
        return "hole_limit";
    end

    -- 计算购买的消耗
    local cost = FormulaM.invoke("CALC_BUY_WORLD_TREE_HOLE_COST", holeAmount);
    local field = cost[1];
    local value = cost[2];
    if ME.user:queryAttrib(field) < value then
        -- 属性不足
        return field .. "_lack";
    end

    return true;
end

-----------------------------
-- 购买世界树孔
function buyWorldTreeHole()
    -- 经判断，不能购买
    if true ~= canBuyWorldTreeHole() then
        return false;
    end

    -- 请求服务器购买
    Operation.cmd_buy_world_tree_hole();

    return true;
end

----------------------------
-- 判断能否重置佩戴的天赋
-- @param option  新的天赋列表
function canSetTalentsOption(option)
    -- 获取所有已经激活的天赋信息
    local talents = ME.user:getTalents();
    if #talents == 0 then
        -- 所有天赋都未激活，不能设置佩戴的天赋
        return false;
    end

    -- 如果是清空列表，不需要判断
    if #option == 0 then
        return true;
    end

    -- 已激活的天赋列表
    local activeList = table.keys(talents);

    -- 判断传入的天赋是否合法
    for _, skillId in pairs(option) do
        if table.indexOf(option, skillId)== -1 then
            -- 包含非法天赋
            return false;
        end
    end

    return true;
end

----------------------------------
-- 重置佩戴的天赋
-- @param option  新的天赋列表
function setTalentsOption(option)
    -- 验证不通过
    if true ~= canSetTalentsOption(option) then
        return false;
    end

    -- 请求服务器重置
    Operation.cmd_set_talents_option(option);

    return true;
end

-- 获得冈布奥的事件回调
function whenGainPet(classId)
    local pet = PetM.getMyPet(classId);

    assert(pet ~= nil);

    -- 尝试激活冈布奥天赋
    local skillId = PetM.query(classId, "talent");
    if skillId == nil then
        -- 没有配置天赋技能
        return;
    end

    -- 判断是否是冈布奥天赋技能
    if not isPetTalent(skillId) then
        assert(false, string.format("冈布奥(%d)的天赋技能配置错误", classId));
        return;
    end

    local rank = PetM.query(classId, "init_rank");
    skillId = SkillM.findSkill(skillId, rank);

    -- 客户端自行激活天赋
    if true ~= activateTalent(skillId, false) then
        trace("TalentM", "激活冈布奥天赋(%d)失败了。", skillId);
        return;
    end
end

-- 获取冈布奥的天赋技能
function getTalentID(initTalentId)
    local talentId = -1;
    local talents = ME.user:getTalents();

    -- 判断传入的天赋是否合法
    for _, skillId in pairs(talents) do
        local baseSkillId = SkillM.findBaseSkill(skillId);
        if baseSkillId == initTalentId then
            talentId = skillId;
            break;
        end
    end
    return talentId;
end

function getTalentByRank(baseTalent, rank)
    return baseTalent + rank - 1;
end
